A hardware object that is suitable for use in an object-based environment generally includes a set of operations and a state that effectively remembers the effect of the operations. Since an object has at least some memory capability, an object differs from a function, which has substantially no memory capability. For example, a value returned by an operation associated with an object is dependent upon the state of the object as well as the arguments to the operation. As such, each invocation of an object may have a different result. In contrast, a value returned by a function is typically dependent only on the arguments to the function.
Within an object-based environment, threads are often used to satisfy requests for services. A thread is essentially a single sequential flow of control within a computer program. In general, a thread, or a “thread of control,” is a sequence of central processing unit (CPU) instructions or programming language statements that may be independently executed. Each thread has its own execution stack on which method activations reside. As will be appreciated by those skilled in the art, when a method is activated with respect to a thread, an activation is pushed on the execution stack of the thread. When the method returns, or is deactivated, the activation is popped from the execution stack. Since an activation of one method may activate another method, an execution stack operates in a first-in-last-out manner.
In object-based environments, only one thread is allowed to invoke one of some number of operations (e.g., synchronized operations) that involve a particular object at any given time. Synchronization constructs, such as locks, are used to control access to shared resources (e.g., objects) such that only a single thread may invoke operations on a shared object at any given time. By way of example, in order to prevent more than one thread from operating on an object at any particular time, objects are often provided with locks. The locks are arranged such that only the thread that has possession of the lock for an object is permitted to execute a method on that object.
Generally, such locks are either global or local. Global locks are locks shared by multiple objects. Local locks are locks allocated separately to each object. More than one object may be locked by one or more threads at any given time. That is, different threads may hold locks to different objects at substantially the same time. For example, a first thread a may obtain a first lock and, hence, lock a first object while a second thread has possession of second lock and, therefore, access to a second object. However, if the first thread wishes to obtain a lock on the second object, then the first thread must wait for the second thread to relinquish control of the second object.
Deadlock, as will be understood by those skilled in the art, is a failure in which two or more threads are stopped while waiting for each other to perform an action. For example, deadlock may occur when a first thread holds the lock on a first object, and next requires that a second object be locked before relinquishing the lock on the first object, while a second thread holds the lock on the second object, and next requires that the first object be locked before relinquishing the lock on the second object.
Traditional methods of object locking generally use global locks. Such methods introduce increased waiting for held locks, which can defeat the purpose of concurrent operations and greatly affect system performance. One alternative to the conventional deadlock prevention rules is to monitor for deadlock conditions externally. This method makes recovery much more difficult and does not provide as much test coverage because the focus is shifted from early deadlock prevention to deadlock detection. Other conventional methods serialize operations to avoid resource conflict; these methods similarly suffer in performance.
Therefore, alternatives are desirable to minimize lock related overhead.